/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010.

    This file is part of Maxsi ImaginationLand.

    Maxsi ImaginationLand is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi ImaginationLand is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi ImaginationLand.  If not, see <http://www.gnu.org/licenses/>.

	If you modify this file, please enter your name below and provide contact
	information in MaxsiImaginationLandContributors.h. For more information please see
	MaxsiImaginationLandContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiImaginationLand.exe
	Imaginary programming!
	
	Main.cpp
	Basic WIN32 functions and stuff

****************************************************************************/

#include "MaxsiLibrary.h"
#include <math.h>
#include "Main.h"

//#define PRINT_RENDER_PROGRESS_TO_STDOUT
#define ANIMATE
//#define ANIMATE_ZOOM
#define ANIMATE_ADJUST_QUALITY_TO_FPS
#define ANIMATE_LOWISH_FPS_IS_OKAY
//#define HD
#define INTERLACED // Each thread draws each n'th line, where n is the amount of CPUs, gives much better performance.
#define SSE
//#define SSE2
#define CACHE_BACKBUFFER_HANDLE
//#define SCROLL_IMRPOVES_QUALITY
#define ENABLE_KEYBOARD_INPUT
#define ENABLE_SOMETHING_REALLY_REALLY_REALLY_STUPID

#if defined(SCROLL_IMRPOVES_QUALITY) || defined(ENABLE_KEYBOARD_INPUT)
#define QUALITY_IS_ADJUSTABLE
#endif

#ifdef SSE
#include <xmmintrin.h>
#ifdef SSE2
#include <emmintrin.h>
#ifdef __MSC_VER
#define SSE2_DoneWith // Enabling this SSE2 code will seriously hit performance when compiled with MinGW - VS seems unaffected.
#endif
#define SSE2_Init_sAA
#endif
#endif

float WINDOW_X = -2.0f;
float WINDOW_Y = 1.5f;
float WINDOW_W = 3.0f;
float WINDOW_H = -3.0f;

#ifdef ANIMATE

	#define MAX_QUALITY 768

	#ifdef ANIMATE_ADJUST_QUALITY_TO_FPS

		#define			ANTIALIAS_QUALITY 2
		#define			ANTIALIAS_QUALITY_IS_TWO_POWER 1
		unsigned int	TRIES_PER_PIXEL = 30;

		#ifdef QUALITY_IS_ADJUSTABLE

			unsigned int	TRIES_PER_PIXEL_ADDED = 0;
			#define SCROLL_INCREASES_QUALITY_WITH 5

		#endif
			
		#ifdef ANIMATE_LOWISH_FPS_IS_OKAY

			#define			IDEAL_FPS 10.0f
			#define			PERFECT_FPS 20.0f

		#else

			#define			IDEAL_FPS 20.0f
			#define			PERFECT_FPS 40.0f

		#endif

		#define			FPS_ADJUST_SPEED 0.7f

	#else

		#define			TRIES_PER_PIXEL 30

	#endif

	#ifdef ANIMATE_ZOOM

		#define			ZOOM_SPEED 0.8f
		#define			ZOOMTO_X -1.74f
		#define			ZOOMTO_Y 0 //-0.0005f

	#else
	
		#define			ZOOM_SPEED 0.5f
		float			WINDOW_IDEAL_X;
		float			WINDOW_IDEAL_Y;
		float			WINDOW_IDEAL_W;
		float			WINDOW_IDEAL_H;

	#endif

#else

	#define			ANTIALIAS_QUALITY 16
	#define			ANTIALIAS_QUALITY_IS_TWO_POWER 4
	#define			TRIES_PER_PIXEL 300

	#ifdef ANIMATE_ZOOM
	#undef ANIMATE_ZOOM
	#endif

#endif

#ifdef ENABLE_SOMETHING_REALLY_REALLY_REALLY_STUPID
bool	SeriouslyYouDontWantToEnableThis	=	false;
#endif


#if 0
COLORREF*			Buffer1			=	NULL;
COLORREF*			Buffer2			=	NULL;
COLORREF*			FrontBuffer		=	NULL;
COLORREF*			BackBuffer		=	NULL;
DWORD				BufferWidth		=	0;
DWORD				BufferHeight	=	0;
DWORD				ZCalls			=	0;
DWORD_PTR			nThreads		=	0;
HANDLE				DrawRequest		=	0;
HANDLE				DrawEvent		=	NULL;
HANDLE				DrawedEvent		=	NULL;
HANDLE*				Thread			=	NULL;
HANDLE*				ThreadDone		=	NULL;
RECT				rectClient;
bool				MouseUpYet		=	true;
#ifdef CACHE_BACKBUFFER_HANDLE
HBITMAP				DrawBackBuffer	=	NULL;
LONG				DrawBackBufferW	=	0;
LONG				DrawBackBufferH	=	0;
#endif
#endif

#ifdef _MSC_VER
#define SetSIMDFloat(Var, Index, Value) Var.m128_f32[Index] = Value
#define GetSIMDFloat(Var, Index) Var.m128_f32[Index]
#define GetSIMDBool(Var, Index) Var.m128_u32[Index]
#else
#define SetSIMDFloat(Var, Index, Value) *((float*)&Var+Index) = Value
#define GetSIMDFloat(Var, Index) *((float*)&Var+Index)
#define GetSIMDBool(Var, Index) *((uint32_t*)&Var+Index)
#endif

GtkWidget*		Window			=	NULL;
GtkWidget*		Frame			=	NULL;
GtkWidget*		ScreenOutput	=	NULL;
GdkPixbuf*		FrontBuffer		=	NULL;
int				Width			=	1280;
int				Height			=	1024;

size_t			LinesRendered	=	0;
MaxsiHandle		LineLock		=	NULL;
MaxsiHandle		RenderedEvent	=	NULL;

#if 0
static void put_pixel(GdkPixbuf *pixbuf, int x, int y, guchar red, guchar green, guchar blue, guchar alpha)
{

	guchar* pixels;
	guchar* p;

	int		n_channels	=	gdk_pixbuf_get_n_channels(pixbuf);

	g_assert(gdk_pixbuf_get_colorspace(pixbuf) == GDK_COLORSPACE_RGB);

	g_assert(gdk_pixbuf_get_bits_per_sample(pixbuf) == 8);

	g_assert(gdk_pixbuf_get_has_alpha(pixbuf));

	g_assert(n_channels == 4);

	int		width		=	gdk_pixbuf_get_width(pixbuf);
	int		height		=	gdk_pixbuf_get_height(pixbuf);

	g_assert(x >= 0 && x < width);
	g_assert(y >= 0 && y < height);

	int		rowstride	=	gdk_pixbuf_get_rowstride(pixbuf);

	pixels		=	gdk_pixbuf_get_pixels(pixbuf);

	p			=	pixels + y * rowstride + x * n_channels;

	p[0] = red;
	p[1] = green;
	p[2] = blue;
	p[3] = alpha;

}
#endif

size_t GlobalDummyVar = 0;

MaxsiError RenderLine(size_t ThreadId, BYTE* Parameter)
{
	// This function is funny because you need a lot of variable it seems.

	size_t		Y			=	(size_t)Parameter;
	GdkPixbuf*	Buffer		=	FrontBuffer;	
	int			n_channels	=	gdk_pixbuf_get_n_channels(Buffer);

	g_assert(gdk_pixbuf_get_colorspace(Buffer) == GDK_COLORSPACE_RGB);
	g_assert(gdk_pixbuf_get_bits_per_sample(Buffer) == 8);
	g_assert(n_channels == 3);

	int			DrawWidth	=	gdk_pixbuf_get_width(Buffer);
	int			DrawHeight	=	gdk_pixbuf_get_height(Buffer);
	int			RowStride	=	gdk_pixbuf_get_rowstride(Buffer);
	guchar*		Pixels		=	gdk_pixbuf_get_pixels(Buffer);
	guchar*		P			=	Pixels + Y * RowStride;


	ComplexFloat	In;
	uint32_t		Count;

#ifdef SSE2_DoneWith
	uint32_t	sCount[4];
	__m128		DoneWith;
#elif defined(SSE)
	uint32_t	sCount[4];
	bool		DoneWith[4];
#endif

	uint32_t	LastCount	=	-1;
	float		LastSQRTCount;	

	float		PixelX		=	WINDOW_X/2.0f;
	float		PixelY		=	WINDOW_Y/2.0f;
	float		XOffset		=	0.0f;
	float		YOffset		=	0.0f;
	float		Colorness;
	float		CountSQRT;
	uint32_t	iColorness;

	uint32_t	ColorR;
	uint32_t	ColorG;
	uint32_t	ColorB;

	size_t		AAQSQR		=	ANTIALIAS_QUALITY*ANTIALIAS_QUALITY;
	
	float	sqrtTRIES_PER_PIXEL		=	(float)TRIES_PER_PIXEL;
	float	sqrtTRIES_PER_PIXELhalf	=	 0.5f*sqrtTRIES_PER_PIXEL;
	float	PixelW		=	WINDOW_W/DrawHeight; // Width of a pixel
	float	PixelH		=	WINDOW_H/DrawHeight; // Height of a pixel
	float	AW			=	PixelW/ANTIALIAS_QUALITY; // Width of antialiasing
	float	AH			=	PixelH/ANTIALIAS_QUALITY; // Height of antialiasing

#ifdef SSE
	__m128		sAW			=	_mm_set_ps1(AW);
	__m128		sAH			=	_mm_set_ps1(AH);
#ifdef SSE2_Init_sAA
	__m128i		sAAMASK		=	_mm_set1_epi32((ANTIALIAS_QUALITY << 1) - 1);
#endif
#endif

	// Magic starts here.
	int i = *(int*)&sqrtTRIES_PER_PIXEL; // get bits for floating value
	i = 0x5f3759df - (i>>1); // gives initial guess y0
	sqrtTRIES_PER_PIXEL = *(float*)&i; // convert bits back to float
	sqrtTRIES_PER_PIXEL = sqrtTRIES_PER_PIXEL*(1.5f-sqrtTRIES_PER_PIXELhalf*sqrtTRIES_PER_PIXEL*sqrtTRIES_PER_PIXEL); // Newton step, repeating increases accuracy

	float	invsqrtTRIES_PER_PIXEL	=	sqrtTRIES_PER_PIXEL;

	for ( int X = 0; X < Width; X++ )
	{
		ColorR	=	0;
		ColorG	=	0;
		ColorB	=	0;

#if 1

#ifdef SSE

		__m128	sXOffset	=	_mm_set_ps1(WINDOW_X+PixelW*X);
		__m128	sYOffset	=	_mm_set_ps1(WINDOW_Y+PixelH*Y);
		__m128	sAA;
		__m128	sOldReal;
		__m128	sReal;
		__m128	sImaginary;
		__m128	sTwo		=	_mm_set_ps1(2.0f);
		__m128	sFour		=	_mm_set_ps1(4.0f);

		for ( size_t I = 0; I < AAQSQR; I += 4 )
		{
			__m128	sZReal		=	_mm_set_ps1(0.0f);
			__m128	sZImaginary	=	_mm_set_ps1(0.0f);

			sCount[0]			=	0;
			sCount[1]			=	0;
			sCount[2]			=	0;
			sCount[3]			=	0;

#ifdef SSE2_DoneWith
			int* DoneWithInt	=	(int*)&DoneWith;
			DoneWithInt[0]		=	0;
			DoneWithInt[1]		=	0;
			DoneWithInt[2]		=	0;
			DoneWithInt[3]		=	0;
#else
			DoneWith[0]			=	false;
			DoneWith[1]			=	false;
			DoneWith[2]			=	false;
			DoneWith[3]			=	false;
#endif

#ifdef SSE2_Init_sAA
			sAA					=	_mm_cvtepi32_ps(_mm_and_si128(_mm_set_epi32(I, I+1, I+2, I+3),sAAMASK));
#else
			SetSIMDFloat(sAA, 0, (float)((I)%ANTIALIAS_QUALITY));
			SetSIMDFloat(sAA, 1, (float)((I+1)%ANTIALIAS_QUALITY));
			SetSIMDFloat(sAA, 2, (float)((I+2)%ANTIALIAS_QUALITY));
			SetSIMDFloat(sAA, 3, (float)((I+3)%ANTIALIAS_QUALITY));
#endif

			sReal				=	_mm_mul_ps(sAW,sAA);
			sReal				=	_mm_add_ps(sXOffset, sReal);

#ifdef SSE2_Init_sAA
			sAA					=	_mm_cvtepi32_ps(_mm_srli_epi32(_mm_set_epi32(I, I+1, I+2, I+3),ANTIALIAS_QUALITY_IS_TWO_POWER));

#else
			SetSIMDFloat(sAA, 0, (float)((I)/ANTIALIAS_QUALITY));
			SetSIMDFloat(sAA, 1, (float)((I+1)/ANTIALIAS_QUALITY));
			SetSIMDFloat(sAA, 2, (float)((I+2)/ANTIALIAS_QUALITY));
			SetSIMDFloat(sAA, 3, (float)((I+3)/ANTIALIAS_QUALITY));
#endif

			sImaginary			=	_mm_mul_ps(sAH,sAA);
			sImaginary			=	_mm_add_ps(sYOffset, sImaginary);

#ifdef  ENABLE_SOMETHING_REALLY_REALLY_REALLY_STUPID
			if ( SeriouslyYouDontWantToEnableThis )
			{
				// sOldReal used as temporary variable
				sOldReal			=	sImaginary;
				sImaginary			=	sReal;
				sReal				=	sOldReal;
			}
#endif

			for ( Count = 0; Count < TRIES_PER_PIXEL; Count++ )
			{
				sOldReal			=	sZReal;

				sZReal				=	_mm_mul_ps(sZReal, sZReal);
				sZReal				=	_mm_sub_ps(sZReal, _mm_mul_ps(sZImaginary, sZImaginary));
				sZReal				=	_mm_add_ps(sZReal, sReal);

				sZImaginary			=	_mm_mul_ps(sZImaginary, _mm_mul_ps(sTwo, sOldReal));
				sZImaginary			=	_mm_add_ps(sZImaginary, sImaginary);

				// (We reuse sOldReal to save memory
				sOldReal			=	_mm_add_ps(_mm_mul_ps(sZReal, sZReal), _mm_mul_ps(sZImaginary, sZImaginary) );

#ifdef SSE2_DoneWith
				DoneWith			=	_mm_or_ps(DoneWith, _mm_cmpgt_ps(sOldReal, sFour));
#else
				DoneWith[0]			=	DoneWith[0] || GetSIMDFloat(sOldReal,0) > 4.0f;
				DoneWith[1]			=	DoneWith[1] || GetSIMDFloat(sOldReal,1) > 4.0f;
				DoneWith[2]			=	DoneWith[2] || GetSIMDFloat(sOldReal,2) > 4.0f;
				DoneWith[3]			=	DoneWith[3] || GetSIMDFloat(sOldReal,3) > 4.0f;
#endif

#ifdef SSE2_DoneWith
				if ( GetSIMDBool(DoneWith, 0) && GetSIMDBool(DoneWith, 1) && GetSIMDBool(DoneWith, 2) && GetSIMDBool(DoneWith, 3) )
#else
				if ( DoneWith[0] && DoneWith[1] && DoneWith[2] && DoneWith[3] )
#endif
				{
					// All the sample points have now 'approached' 'infinity', break loop!
					break;
				}
						
				// The else is to avoid overflows.
				if ( GetSIMDFloat(sOldReal,0) <= 4.0f )	{ sCount[0]++; } else { SetSIMDFloat(sZReal, 0, 4.0f); SetSIMDFloat(sZImaginary, 0, 4.0f); }
				if ( GetSIMDFloat(sOldReal,1) <= 4.0f ) { sCount[1]++; } else { SetSIMDFloat(sZReal, 1, 4.0f); SetSIMDFloat(sZImaginary, 1, 4.0f); }
				if ( GetSIMDFloat(sOldReal,2) <= 4.0f ) { sCount[2]++; } else { SetSIMDFloat(sZReal, 2, 4.0f); SetSIMDFloat(sZImaginary, 2, 4.0f); }
				if ( GetSIMDFloat(sOldReal,3) <= 4.0f ) { sCount[3]++; } else { SetSIMDFloat(sZReal, 3, 4.0f); SetSIMDFloat(sZImaginary, 3, 4.0f); }
			}

#ifdef ENABLE_SOMETHING_REALLY_REALLY_REALLY_STUPID

			uint32_t R_A = 64;
			uint32_t R_B = 4;
			uint32_t G_A = 128;
			uint32_t G_B = 2;
			uint32_t B_A = 256;
			uint32_t B_B = 1;

			if ( SeriouslyYouDontWantToEnableThis )
			{
				R_A = 64;
				R_B = 4;
				G_A = 128;
				G_B = 1;
				B_A = 128;
				B_B = 1;
			}
#else
			#define R_A 64
			#define R_B 4
			#define G_A 128
			#define G_B 2
			#define B_A 256
			#define B_B 1
#endif

	uint32_t Tries = TRIES_PER_PIXEL;

	// Give stuff a color!
	for ( size_t N = 0; N < 4; N++ )
	{
		if ( sCount[N] != TRIES_PER_PIXEL )
		{
			if ( sCount[N] > 255 ) { sCount[N] = 255; }

			iColorness = sCount[N] * 256 / Tries;

				if ( iColorness < B_A )
				{
					ColorB			+=	B_B*iColorness;

					if ( iColorness < G_A )
					{
						ColorG		+=	G_B*iColorness;

						if ( iColorness < R_A )
						{
							ColorR	+=	R_B*iColorness;
						}
						else
						{
							ColorR	+=	255;
						}
					}
					else
					{
						ColorG		+=	255;
						ColorR		+=	255;
					}
				}
				else
				{
					ColorB			+=	255;
					ColorG			+=	255;
					ColorR			+=	255;
				}
			}
		}
	}

#else
		XOffset	=	WINDOW_X+PixelW*X;
		YOffset	=	WINDOW_Y+PixelH*Y;

		for ( uint32_t Ax = 0; Ax < (ANTIALIAS_QUALITY*ANTIALIAS_QUALITY); Ax++ )
		{
			In.Real			=	XOffset+(AW*(Ax%ANTIALIAS_QUALITY));
			In.Imaginary	=	YOffset+(AH*(Ax/ANTIALIAS_QUALITY));

			float	OldReal;
			ComplexFloat z(0,0);

			for ( Count = 0; Count < TRIES_PER_PIXEL; Count++ )
			{
				OldReal				=	z.Real;
				z.Real				*=	z.Real;
				z.Real				-=	z.Imaginary*z.Imaginary - In.Real;
				z.Imaginary			*=	2.0f*OldReal;
				z.Imaginary			+=	In.Imaginary;
							
				if ( 
					// Todo, we have some magic constants here, they should be more 'precise'! :D
					(Count == 0 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.0625 ) ||
					(Count == 1 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.035226546f ) ||
					(Count == 2 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.0010055788f ) /*||
					(Count == 3 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.00019491167f ) ||
					(Count == 4 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.00021877403f )*/  )
				{
					Count	=	-1;
					break;
				}
						
				if ( z.Real*z.Real+z.Imaginary+z.Imaginary > 4.0f )
				{
					break;
				}
			}

			if ( Count != ULONG_MAX )
			{
				Count++;
				ZCalls	+=	Count+1;

				if ( Count == TRIES_PER_PIXEL+1 ) { Count = 0; }
			}

			if ( Count != ULONG_MAX )
			{
				if ( Count > 255 ) { Count = 255; }

				int	Tries;

				if ( TRIES_PER_PIXEL > 255 ) { Tries = 255; } else { Tries = TRIES_PER_PIXEL; }

				iColorness = Count * 256 / Tries;

				if ( iColorness < 256 )
				{
					ColorB			+=	iColorness;

					if ( iColorness < 128 )
					{
						ColorG		+=	2*iColorness;

						if ( iColorness < 64 )
						{
							ColorR	+=	4*iColorness;
						}
						else
						{
							ColorR	+=	255;
						}
					}
					else
					{
						ColorG		+=	255;
						ColorR		+=	255;
					}
				}
				else
				{
					ColorB			+=	255;
					ColorG			+=	255;
					ColorR			+=	255;
				}
			}

			if ( false && Count != ULONG_MAX  )
			{
				if ( LastCount == Count )
				{
					Colorness		=	LastSQRTCount*invsqrtTRIES_PER_PIXEL;
				}
				else
				{
					CountSQRT		=	sqrt((float)Count);
					LastSQRTCount	=	CountSQRT;
					Colorness		=	CountSQRT*LastSQRTCount;

					LastCount		=	Count;
				}

				iColorness		=	(unsigned int)128.0f*Colorness;

				if ( iColorness > 255 ) { iColorness = 255; }

				ColorR			+=	iColorness;
				ColorG			+=	iColorness;
				ColorB			+=	iColorness;
			}
		}
#endif

		ColorR /= AAQSQR;
		ColorG /= AAQSQR;
		ColorB /= AAQSQR;		

		P[0] = ColorR;
		P[1] = ColorG;
		P[2] = ColorB;

#else
		size_t Big = 0;

		for ( size_t I = 0; I < 64; I++ )
		{
			Big += I*I - I/(2*(I+1));
		}

		GlobalDummyVar = Big;

		P[0] = (((ThreadId+1)*32) % 128 + 127);
		P[1] = (((ThreadId+2)*32) % 128 + 127);
		P[2] = (((ThreadId+3)*32) % 128 + 127);
#endif

		P += n_channels;
	}

	ThreadSystem()->EnterCriticalSection(LineLock);
	
	LinesRendered++;

#ifdef PRINT_RENDER_PROGRESS_TO_STDOUT
	printf("Thread %zu: Rendered line %zu (%zu of %i lines rendered)\n", ThreadId, Y, LinesRendered, Height);
#endif

	if ( LinesRendered == (size_t)Height )
	{
		ThreadSystem()->SignalEvent(RenderedEvent);
	} 

	ThreadSystem()->LeaveCriticalSection(LineLock);





	return MAXSI_ERROR_SUCCESS;
}


MaxsiError RenderToFrontBuffer()
{
	LinesRendered	=	0;

	for ( int I = 0; I < Height; I++ )
	{
		ScheduleWorkerThreadJob(RenderLine, (BYTE*)I);
	}

	ThreadSystem()->WaitForEvent(RenderedEvent);
	ThreadSystem()->WaitedForEvent(RenderedEvent);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError UpdateScene(double FPS, double DeltaTime)
{
	#ifdef ANIMATE
		#ifdef ANIMATE_ZOOM

			float	SpeedFactor		=	1.0f-(1.0f-ZOOM_SPEED)*DeltaTime;

			WINDOW_X	=	((WINDOW_X-ZOOMTO_X) * SpeedFactor)+ZOOMTO_X;
			WINDOW_Y	=	((WINDOW_Y-ZOOMTO_Y) * SpeedFactor)+ZOOMTO_Y;
			WINDOW_H	=	WINDOW_H * SpeedFactor;
			WINDOW_W	=	WINDOW_W * SpeedFactor;

		#else
				
			float	SpeedFactor		=	1.0f-(1.0f-ZOOM_SPEED)*DeltaTime;

			WINDOW_X	=	((WINDOW_X-WINDOW_IDEAL_X) * SpeedFactor)+WINDOW_IDEAL_X;
			WINDOW_Y	=	((WINDOW_Y-WINDOW_IDEAL_Y) * SpeedFactor)+WINDOW_IDEAL_Y;
			WINDOW_W	=	((WINDOW_W-WINDOW_IDEAL_W) * SpeedFactor)+WINDOW_IDEAL_W;
			WINDOW_H	=	((WINDOW_H-WINDOW_IDEAL_H) * SpeedFactor)+WINDOW_IDEAL_H;

		#endif

		#ifdef ANIMATE_ADJUST_QUALITY_TO_FPS

			unsigned int MIN_QUALITY = 20;

			#ifdef QUALITY_IS_ADJUSTABLE

				MIN_QUALITY		+=	TRIES_PER_PIXEL_ADDED;

			#endif

			if ( MIN_QUALITY > MAX_QUALITY ) { MIN_QUALITY = MAX_QUALITY; }

			if ( FPS < IDEAL_FPS )
			{
				if ( TRIES_PER_PIXEL > MIN_QUALITY )
				{
					TRIES_PER_PIXEL		=	(unsigned int)(TRIES_PER_PIXEL*FPS_ADJUST_SPEED);
				}
			}
			else if ( FPS > PERFECT_FPS )
			{
				if ( TRIES_PER_PIXEL < MAX_QUALITY )
				{
					TRIES_PER_PIXEL		=	(unsigned int)(TRIES_PER_PIXEL/FPS_ADJUST_SPEED);
				}
				else
				{
					TRIES_PER_PIXEL		=	MAX_QUALITY;
				}
			}

			if ( MIN_QUALITY > TRIES_PER_PIXEL ) { TRIES_PER_PIXEL = MIN_QUALITY; }

		#endif
	#endif

	#ifdef SSE2
		#define COMPILE_STRING _MESTR("SSE2 ")
	#elif defined(SSE)
		#define COMPILE_STRING _MESTR("SSE ")
	#else
		#define COMPILE_STRING _MESTR("NO-SSE")
	#endif

	MESTR*	QualityString	=	new MESTR[UIntLength10(TRIES_PER_PIXEL)];

	QualityString[PrintUIntM10(QualityString, TRIES_PER_PIXEL)] = 0;

	if ( FPS > 1.0f )
	{
		MESTR*	FPSStr	=	new MESTR[UIntLength10((unsigned int)FPS)];

		FPSStr[PrintUIntM10(FPSStr, (unsigned int)FPS)] = 0;

		MESTR*	WindowTitle		=	BuildString(7, _MESTR("Maxsi ImaginationLand "), COMPILE_STRING, _MESTR("Q="), QualityString, _MESTR(" "),  FPSStr, _MESTR(" fps"));

		char*	WindowTitleSL	=	MESTR2CHAR(WindowTitle);
		
		gtk_window_set_title(GTK_WINDOW(Window), WindowTitleSL);

		delete[]	WindowTitleSL;
		delete[]	FPSStr;
	}
	else
	{
		FPS				=	1/FPS;

		MESTR*	FPSStr	=	new MESTR[UIntLength10((unsigned int)FPS)];

		FPSStr[PrintUIntM10(FPSStr, (unsigned int)FPS)] = 0;

		MESTR*	WindowTitle		=	BuildString(7, _MESTR("Maxsi ImaginationLand "), COMPILE_STRING, _MESTR("Q="), QualityString, _MESTR(" "), FPSStr, _MESTR(" seconds per frame"));

		char*	WindowTitleSL	=	MESTR2CHAR(WindowTitle);
		
		gtk_window_set_title(GTK_WINDOW(Window), WindowTitleSL);

		delete[]	WindowTitleSL;
		delete[]	WindowTitle;
		delete[]	FPSStr;
	}

	delete[]	QualityString;

	return MAXSI_ERROR_SUCCESS;
}

int timeval_subtract(struct timeval* result, struct timeval* x, struct timeval* y)
{
	// Perform the carry for the later subtraction by updating y.
	if (x->tv_usec < y->tv_usec)
	{
		int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
		y->tv_usec -= 1000000 * nsec;
		y->tv_sec += nsec;
	}
	if (x->tv_usec - y->tv_usec > 1000000)
	{
		int nsec = (x->tv_usec - y->tv_usec) / 1000000;
		y->tv_usec += 1000000 * nsec;
		y->tv_sec -= nsec;
	}
     
	// Compute the time remaining to wait. tv_usec is certainly positive.
	result->tv_sec		=	x->tv_sec - y->tv_sec;
	result->tv_usec		=	x->tv_usec - y->tv_usec;
     
	// Return 1 if result is negative.
	return x->tv_sec < y->tv_sec;
}

double timeval_to_double(struct timeval Input)
{
	return (double)(Input.tv_sec*1.0f + Input.tv_usec / 1000000.0f);
}

gboolean expose_event_callback(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
	struct timeval Begun;
	struct timeval End;
	struct timeval Elapsed;

	gettimeofday(&Begun, NULL);

	RenderToFrontBuffer();

	gettimeofday(&End, NULL);

	timeval_subtract(&Elapsed, &End, &Begun);

	gdk_draw_pixbuf(widget->window, widget->style->fg_gc[gtk_widget_get_state(widget)],
		FrontBuffer,
		0, 0,
		0, 0,
		-1, -1,
		GDK_RGB_DITHER_NONE, 0, 0);

	double	DeltaTime	=	timeval_to_double(Elapsed);
	
	UpdateScene(1.0f / DeltaTime, DeltaTime);

#ifdef ANIMATE
	// Request another draw!
	gtk_widget_queue_draw(GTK_WIDGET(widget));
#endif

	return TRUE;
}

gint button_press_event_callback(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
	// Make sure we have focus now we've been selected

	if ( !GTK_WIDGET_HAS_FOCUS(widget)) 
	{
		gtk_widget_grab_focus(widget);
	}
	
    // Don't let additional events about multiple clicks sent by GTK to us after the initial button press event confuse us.
    //if (event->type != GDK_BUTTON_PRESS) { return FALSE; }

	int	xPos = event->x;
	int yPos = event->y;

	if ( event->button == 1 ) // Left button
	{
		float	fxPos	=	WINDOW_X + ((WINDOW_W * (float)xPos) / (float)Width );
		float	fyPos	=	WINDOW_Y + ((WINDOW_H * (float)yPos) / (float)Height );

		WINDOW_IDEAL_X		=	fxPos-0.25f*WINDOW_W;
		WINDOW_IDEAL_Y		=	fyPos-0.25f*WINDOW_H;
		WINDOW_IDEAL_W		=	0.5f*WINDOW_W;
		WINDOW_IDEAL_H		=	0.5f*WINDOW_H;
		
	}
	else if ( event->button == 2 ) // Middle button
	{
		float	fxPos	=	WINDOW_X + ((WINDOW_W * (float)xPos) / (float)Width );
		float	fyPos	=	WINDOW_Y + ((WINDOW_H * (float)yPos) / (float)Height );

		WINDOW_IDEAL_X		=	fxPos-0.5f*WINDOW_W;
		WINDOW_IDEAL_Y		=	fyPos-0.5f*WINDOW_H;
	}
	else if ( event->button == 3 ) // Right button
	{

		float	fxPos	=	WINDOW_X + ((WINDOW_W * (float)xPos) / (float)Width );
		float	fyPos	=	WINDOW_Y + ((WINDOW_H * (float)yPos) / (float)Height );

		WINDOW_IDEAL_X		=	fxPos-WINDOW_W;
		WINDOW_IDEAL_Y		=	fyPos-WINDOW_H;
		WINDOW_IDEAL_W		=	2.0f*WINDOW_W;
		WINDOW_IDEAL_H		=	2.0f*WINDOW_H;
	}	
	
    return TRUE;
}

static gboolean HandleMouseScrollWheel(GtkWidget * pWidget, GdkEventScroll* pEvent, gpointer pUserData )
{
	if ( pEvent->direction == GDK_SCROLL_UP )
	{
		TRIES_PER_PIXEL_ADDED += 5;
	}
	if ( pEvent->direction == GDK_SCROLL_DOWN )
	{
		if ( TRIES_PER_PIXEL_ADDED > 5 ) { TRIES_PER_PIXEL_ADDED -= 5; } else { TRIES_PER_PIXEL_ADDED = 0; }
	}

	return true; // false means continue propagating the event
}

bool CreateWindow()
{
	Window			=	gtk_window_new(GTK_WINDOW_TOPLEVEL);

	g_signal_connect_swapped(G_OBJECT(Window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
	//g_signal_connect(G_OBJECT(Window), "configure-event", G_CALLBACK(WindowResized), NULL);

	gtk_window_set_title(GTK_WINDOW(Window), "Maxsi ImaginationLand");
	gtk_window_set_default_size(GTK_WINDOW(Window), Width, Height);
	gtk_window_set_position(GTK_WINDOW(Window), GTK_WIN_POS_CENTER);
	gtk_widget_add_events(GTK_WIDGET(Window), GDK_CONFIGURE);

	Frame			=	gtk_fixed_new();

 	gtk_container_add(GTK_CONTAINER(Window), Frame);

	ScreenOutput	=	gtk_drawing_area_new();

	gtk_widget_add_events(ScreenOutput, GDK_SCROLL_MASK);
	gtk_widget_add_events(ScreenOutput, GDK_BUTTON_PRESS_MASK);

	g_signal_connect(G_OBJECT(ScreenOutput), "button_press_event", G_CALLBACK(button_press_event_callback), NULL);
	g_signal_connect(G_OBJECT(ScreenOutput), "expose_event", G_CALLBACK(expose_event_callback), NULL);
	g_signal_connect(G_OBJECT(ScreenOutput), "scroll_event", G_CALLBACK(HandleMouseScrollWheel), NULL); 

	ResizeWindow();

	gtk_widget_show_all(Window);

	gtk_window_get_size(GTK_WINDOW(Window), &Width, &Height);

	return true;
}

bool ResizeWindow()
{
	gtk_widget_set_size_request(GTK_WIDGET((GtkWidget*)ScreenOutput), Width, Height);
	gtk_fixed_put(GTK_FIXED(Frame), GTK_WIDGET((GtkWidget*)ScreenOutput), 0, 0);

	return true;
}

void WindowResized(GtkWindow *window, GdkEvent *event, gpointer data)
{
	if ( Width != event->configure.width || Height != event->configure.height )
	{
		Width	=	event->configure.width;
		Height	=	event->configure.height;

		ResizeWindow();
	}
}

int main(int argc, char** argv)
{
	#ifdef ANIMATE
	#ifndef ANIMATE_ZOOM

		WINDOW_IDEAL_X		=	WINDOW_X;
		WINDOW_IDEAL_Y		=	WINDOW_Y;
		WINDOW_IDEAL_W		=	WINDOW_W;
		WINDOW_IDEAL_H		=	WINDOW_H;

		WINDOW_X			*=	2.0f;

		WINDOW_Y			*=	2.0f;
		WINDOW_W			*=	2.0f;
		WINDOW_H			*=	2.0f;

	#endif
	#endif

	if ( CreateWorkerThreadPool() != MAXSI_ERROR_SUCCESS ) { printf("Something went wrong in CreateWorkerThreadPool()!\n"); return 1; }

	MAXSI_TODO("Add error detection and error handling in this function!");

	LineLock		=	ThreadSystem()->CreateCriticalSection();
	RenderedEvent	=	ThreadSystem()->CreateEvent();

	// Initialize GTK
	gtk_init(&argc, &argv);

	FrontBuffer		=	gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, Width, Height);

	CreateWindow();

	gtk_main();

	ThreadSystem()->DeleteEvent(RenderedEvent);
	ThreadSystem()->DeleteCriticalSection(LineLock);

	DeleteWorkerThreadPool();

	return 0;
}

#if 0

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int WINAPI MaxsiImaginationLand::WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow)
{
	//BenchMarkZ();
	//TestSlowInvSqrt();
	//TestFastInvSqrt();
		
	nThreads	=	GetNumberOfLogicalCPUs();
	
#ifdef ANIMATE
#ifndef ANIMATE_ZOOM

	WINDOW_IDEAL_X		=	WINDOW_X;
	WINDOW_IDEAL_Y		=	WINDOW_Y;
	WINDOW_IDEAL_W		=	WINDOW_W;
	WINDOW_IDEAL_H		=	WINDOW_H;

	WINDOW_X			*=	2.0f;
	WINDOW_Y			*=	2.0f;
	WINDOW_W			*=	2.0f;
	WINDOW_H			*=	2.0f;

#endif
#endif

	return BaseClass::WinMain(hInstance,hPrevInstance,lpCmdLine,nCmdShow);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)
    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL MaxsiImaginationLand::InitApplication(
    HANDLE hInstance)
{
    WNDCLASS  wc = {0};

    wc.lpfnWndProc = (WNDPROC) MaxsiImaginationLandProc;
    wc.hInstance = (HINSTANCE)hInstance;
    wc.hIcon = LoadIconA((HINSTANCE)hInstance, "ProductIcon");
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.lpszClassName = TEXT("MaxsiImaginationLand");

	if (!RegisterClass(&wc)) { return FALSE; }

	return TRUE;
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)
    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/
BOOL MaxsiImaginationLand::InitInstance(
    HINSTANCE hInstance, 
    INT nCmdShow)
{
	//nCmdShow	=	SW_SHOWMAXIMIZED;

    BOOL bSuccess = TRUE;

    hInst = hInstance;

    hAccTable = LoadAccelerators(hInst, TEXT("MaxsiImaginationLand"));

	// Get the screen size
	DEVMODE Dev;
	BOOL	Result	=	EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&Dev);
#ifdef HD
	int w = 1280;
	int h = 920;
#else
	
	int ScaleDown = 2;

	if ( nThreads < 2 ) { ScaleDown = 3; }

	int w = Dev.dmPelsWidth/ScaleDown;
	int h = Dev.dmPelsHeight/ScaleDown;

#endif
	int x = (Result) ? (int)(Dev.dmPelsWidth - w ) / 2 : (int)CW_USEDEFAULT;
	int y = (Result) ? (int)(Dev.dmPelsHeight - h ) / 2 : (int)CW_USEDEFAULT;

	char*	WindowTitle		=	BuildString(1,"Maxsi ImaginationLand");

    WindowHWND = CreateWindowA(
        "MaxsiImaginationLand",
        WindowTitle,
        (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX ),
        x, y,
        w, h,
        NULL, NULL, hInstance, NULL );

	delete[] WindowTitle;

	if (!WindowHWND) { return FALSE; }

	DrawRequest	=	CreateEvent(NULL,TRUE,FALSE,NULL);

	CreateThread(NULL,NULL,Imagine,(LPVOID)0,0,0);

	ShowWindow(WindowHWND, nCmdShow);
    bSuccess = UpdateWindow(WindowHWND);

	return bSuccess;
}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
    PURPOSE:  Processes messages

****************************************************************************/

LRESULT APIENTRY MaxsiImaginationLand::WindowProc(
    HWND hWnd, 
    UINT message, 
    WPARAM wParam, 
    LPARAM lParam)
{

	if ( message == WM_MOUSEWHEEL )
	{
#ifdef SCROLL_IMRPOVES_QUALITY
		if ( GET_WHEEL_DELTA_WPARAM(wParam) > 0 )
		{
			if ( TRIES_PER_PIXEL_ADDED < MAX_QUALITY ) { TRIES_PER_PIXEL_ADDED += SCROLL_INCREASES_QUALITY_WITH; }
		}
		else
		{
			if ( TRIES_PER_PIXEL_ADDED >= SCROLL_INCREASES_QUALITY_WITH ) { TRIES_PER_PIXEL_ADDED -= SCROLL_INCREASES_QUALITY_WITH; }
		}
#elif defined(SCROOL_ZOOMS)
		if ( GET_WHEEL_DELTA_WPARAM(wParam) > 0 )
		{
			message		=	WM_LBUTTONDOWN;
		}
		else
		{
			message		=	WM_RBUTTONDOWN;
		}
#endif
	}

	switch (message) 
	{
#ifdef ANIMATE
#ifndef ANIMATE_ZOOM
		#ifdef ENABLE_KEYBOARD_INPUT
			case WM_KEYDOWN:
			{
				LPARAM	Repeats	=	lParam & 0x0F;

				for ( Repeats = Repeats; Repeats; Repeats-- )
				{
					if ( wParam == '1' )
					{
						if ( TRIES_PER_PIXEL_ADDED < MAX_QUALITY ) { TRIES_PER_PIXEL_ADDED += SCROLL_INCREASES_QUALITY_WITH; }					
					}
					else if ( wParam == '2' )
					{
						if ( TRIES_PER_PIXEL_ADDED >= SCROLL_INCREASES_QUALITY_WITH ) { TRIES_PER_PIXEL_ADDED -= SCROLL_INCREASES_QUALITY_WITH; }
					}
#ifdef ENABLE_SOMETHING_REALLY_REALLY_REALLY_STUPID
					else if ( wParam == 'T' )
					{
						SeriouslyYouDontWantToEnableThis	=	!SeriouslyYouDontWantToEnableThis;
					}
#endif
				}
			}
			break;
		#endif
			case WM_LBUTTONDOWN:
			{
				LONG	DrawWidth	=	rectClient.right;
				LONG	DrawHeight	=	rectClient.bottom;
				if ( rectClient.right > BufferWidth ) { DrawWidth = BufferWidth; }
				if ( rectClient.bottom > DrawHeight ) { DrawHeight = BufferHeight; }

				LPARAM	xPos	=	GET_X_LPARAM(lParam); 
				LPARAM	yPos	=	GET_Y_LPARAM(lParam);

				float	fxPos	=	WINDOW_X + ((WINDOW_W * (float)xPos) / (float)DrawWidth );
				float	fyPos	=	WINDOW_Y + ((WINDOW_H * (float)yPos) / (float)DrawHeight );

				WINDOW_IDEAL_X		=	fxPos-(0.25f*WINDOW_W);
				WINDOW_IDEAL_Y		=	fyPos-(0.25f*WINDOW_H);
				WINDOW_IDEAL_W		=	0.5f*WINDOW_W;
				WINDOW_IDEAL_H		=	0.5f*WINDOW_H;

				return 0;
			}
			break;

			case WM_RBUTTONDOWN:
			{
				LONG	DrawWidth	=	rectClient.right;
				LONG	DrawHeight	=	rectClient.bottom;
				if ( rectClient.right > BufferWidth ) { DrawWidth = BufferWidth; }
				if ( rectClient.bottom > DrawHeight ) { DrawHeight = BufferHeight; }

				LPARAM	xPos	=	GET_X_LPARAM(lParam); 
				LPARAM	yPos	=	GET_Y_LPARAM(lParam);

				float	fxPos	=	WINDOW_X + ((WINDOW_W * (float)xPos) / (float)DrawWidth );
				float	fyPos	=	WINDOW_Y + ((WINDOW_H * (float)yPos) / (float)DrawHeight );

				WINDOW_IDEAL_X		=	fxPos-WINDOW_W;
				WINDOW_IDEAL_Y		=	fyPos-WINDOW_H;
				WINDOW_IDEAL_W		=	2.0f*WINDOW_W;
				WINDOW_IDEAL_H		=	2.0f*WINDOW_H;

				return 0;
			}
			break;

			case WM_LBUTTONUP:
			case WM_RBUTTONUP:

				MouseUpYet	=	true;
				break;
#endif
#endif
			case WM_SIZE:
				
				RECT r;
				r.right=LOWORD(lParam);
				r.bottom=HIWORD(lParam);
				r.top=0;
				r.left=0;
				RedrawWindow(WindowHWND,&r,0, RDW_INVALIDATE | RDW_NOINTERNALPAINT | RDW_NOCHILDREN | RDW_ERASE);
				break;

			case WM_PAINT:
				SetEvent(DrawRequest);
				break;

		default:			
			return (BaseClass::WindowProc(hWnd, message, wParam, lParam));
	}
	return (0);

	return BaseClass::WindowProc(hWnd,message,wParam,lParam);
}


DWORD MaxsiImaginationLand::OnThreadRegistered(HWND hWnd)
{
	PostMessage(hWnd,WM_COMMAND,0,0);

	return BaseClass::OnThreadRegistered(hWnd);
}

/*__forceinline*/ ComplexFloat Z(ComplexFloat In, ComplexFloat C)
{/*
	In *= In;
	In += C;
	return In;
	*/
	return (In*In)+C;
}


/*__forceinline*/ void ZFast(ComplexFloat &z, ComplexFloat &C)
{ 
	float OldReal		=	z.Real;
	z.Real				*=	z.Real;
	z.Real				-=	z.Imaginary*z.Imaginary;
	z.Imaginary			*=	2.0f*OldReal;
	z.Imaginary			+=	C.Imaginary;
}


unsigned int	Iterations(ComplexFloat In)
{
	unsigned int Count = 0;
	bool HitOnce = false;
	ComplexFloat Cache(0,0);
	float	OldReal;
	ComplexFloat z(0,0);

	for ( Count = 0; Count < TRIES_PER_PIXEL; Count++ )
	{
		//z = Z(z,In);
		//ZFast(z,In);

		OldReal				=	z.Real;
		z.Real				*=	z.Real;
		z.Real				-=	z.Imaginary*z.Imaginary - In.Real;
		z.Imaginary			*=	2.0f*OldReal;
		z.Imaginary			+=	In.Imaginary;

		if ( Count % 4 == 0 )
		{
			if ( HitOnce )
			{
				if ( Cache == z )
				{
					ZCalls	+=	Count+1;
					return -1;
				}
			}
			else
			{
				HitOnce = true;
			}
			Cache	= z;
		}

		if ( z.Real > 2.0f || z.Real < -2.0f || z.Imaginary > 2.0f || z.Imaginary < -2.0f )
		{
			break;
		}
	}
	Count++;
	ZCalls	+=	Count+1;
	if ( Count == TRIES_PER_PIXEL+1 ) { Count = 0; }

	return Count;
}



DWORD_PTR WINAPI ImagineThread(LPVOID p)
{
	_controlfp(0, _EM_ZERODIVIDE | _EM_INVALID );

	DWORD_PTR		ThreadId	=	(DWORD_PTR)p;
	ComplexFloat	In;
	unsigned int	Count;
#ifdef SSE2_DoneWith
	//__m128i			sCount;
	unsigned int	sCount[4];
	__m128			DoneWith;
#elif defined(SSE)
	unsigned int	sCount[4];
	bool			DoneWith[4];
#endif
	unsigned int	LastCount	=	-1;
	float			LastSQRTCount;	

	float	PixelX		=	WINDOW_X/2.0f;
	float	PixelY		=	WINDOW_Y/2.0f;
	float	XOffset		=	0.0f;
	float	YOffset		=	0.0f;
	float	Colorness;
	float	CountSQRT;
	unsigned int	iColorness;
	unsigned int	ColorR;
	unsigned int	ColorG;
	unsigned int	ColorB;

	LONG	DrawWidth;
	LONG	DrawHeight;

	while ( WaitForSingleObject(DrawEvent,INFINITE) == WAIT_OBJECT_0 )
	{
		size_t		AAQSQR		=	ANTIALIAS_QUALITY*ANTIALIAS_QUALITY;

		DrawWidth	=	rectClient.right;
		DrawHeight	=	rectClient.bottom;
		if ( rectClient.right > BufferWidth ) { DrawWidth = BufferWidth; }
		if ( rectClient.bottom > DrawHeight ) { DrawHeight = BufferHeight; }

		float	sqrtTRIES_PER_PIXEL		=	(float)TRIES_PER_PIXEL;
		float	sqrtTRIES_PER_PIXELhalf	=	 0.5f*sqrtTRIES_PER_PIXEL;
		float	PixelW		=	WINDOW_W/DrawHeight; // Width of a pixel
		float	PixelH		=	WINDOW_H/DrawHeight; // Height of a pixel
		float	AW			=	PixelW/ANTIALIAS_QUALITY; // Width of antialiasing
		float	AH			=	PixelH/ANTIALIAS_QUALITY; // Height of antialiasing

#ifdef SSE
		__m128		sAW			=	_mm_set_ps1(AW);
		__m128		sAH			=	_mm_set_ps1(AH);
#ifdef SSE2_Init_sAA
		__m128i		sAAMASK		=	_mm_set1_epi32((ANTIALIAS_QUALITY << 1) - 1);
#endif
#endif

		// Magic starts here.
		int i = *(int*)&sqrtTRIES_PER_PIXEL; // get bits for floating value
		i = 0x5f3759df - (i>>1); // gives initial guess y0
		sqrtTRIES_PER_PIXEL = *(float*)&i; // convert bits back to float
		sqrtTRIES_PER_PIXEL = sqrtTRIES_PER_PIXEL*(1.5f-sqrtTRIES_PER_PIXELhalf*sqrtTRIES_PER_PIXEL*sqrtTRIES_PER_PIXEL); // Newton step, repeating increases accuracy
		float	invsqrtTRIES_PER_PIXEL	=	sqrtTRIES_PER_PIXEL;

#ifndef INTERLACED
		DWORD_PTR	HeightStart		=	(ThreadId)*DrawHeight/nThreads;
		DWORD_PTR	HeightEnd		=	(ThreadId+1)*DrawHeight/nThreads;

		for ( DWORD_PTR Y = HeightStart; Y < HeightEnd; Y++ )
		{
#else
		for ( DWORD_PTR Y = ThreadId; Y < DrawHeight; Y += nThreads )
		{
#endif
			for ( unsigned int X = 0; X < (unsigned int)DrawWidth; X++ )
			{
				ColorR	=	0;
				ColorG	=	0;
				ColorB	=	0;

#ifdef SSE


				__m128		sXOffset	=	_mm_set_ps1(WINDOW_X+PixelW*X);
				__m128		sYOffset	=	_mm_set_ps1(WINDOW_Y+PixelH*Y);
				__m128		sAA;
				__m128		sOldReal;
				__m128		sReal;
				__m128		sImaginary;
				__m128		sTwo		=	_mm_set_ps1(2.0f);
				__m128		sFour		=	_mm_set_ps1(4.0f);

				for ( size_t I = 0; I < AAQSQR; I += 4 )
				{
					__m128	sZReal		=	_mm_set_ps1(0.0f);
					__m128	sZImaginary	=	_mm_set_ps1(0.0f);

					sCount[0]			=	0;
					sCount[1]			=	0;
					sCount[2]			=	0;
					sCount[3]			=	0;

#ifdef SSE2_DoneWith
					//sCount				=	_mm_set_epi32(0, 0, 0, 0);
					int* DoneWithInt	=	(int*)&DoneWith;
					DoneWithInt[0]		=	0;
					DoneWithInt[1]		=	0;
					DoneWithInt[2]		=	0;
					DoneWithInt[3]		=	0;
#else
					DoneWith[0]			=	false;
					DoneWith[1]			=	false;
					DoneWith[2]			=	false;
					DoneWith[3]			=	false;
#endif

#ifdef SSE2_Init_sAA
					sAA					=	_mm_cvtepi32_ps(_mm_and_si128(_mm_set_epi32(I, I+1, I+2, I+3),sAAMASK));
#else
					SetSIMDFloat(sAA, 0, (float)((I)%ANTIALIAS_QUALITY));
					SetSIMDFloat(sAA, 1, (float)((I+1)%ANTIALIAS_QUALITY));
					SetSIMDFloat(sAA, 2, (float)((I+2)%ANTIALIAS_QUALITY));
					SetSIMDFloat(sAA, 3, (float)((I+3)%ANTIALIAS_QUALITY));
#endif

					sReal				=	_mm_mul_ps(sAW,sAA);
					sReal				=	_mm_add_ps(sXOffset, sReal);

#ifdef SSE2_Init_sAA
					sAA					=	_mm_cvtepi32_ps(_mm_srli_epi32(_mm_set_epi32(I, I+1, I+2, I+3),ANTIALIAS_QUALITY_IS_TWO_POWER));

#else
					SetSIMDFloat(sAA, 0, (float)((I)/ANTIALIAS_QUALITY));
					SetSIMDFloat(sAA, 1, (float)((I+1)/ANTIALIAS_QUALITY));
					SetSIMDFloat(sAA, 2, (float)((I+2)/ANTIALIAS_QUALITY));
					SetSIMDFloat(sAA, 3, (float)((I+3)/ANTIALIAS_QUALITY));
#endif

					sImaginary			=	_mm_mul_ps(sAH,sAA);
					sImaginary			=	_mm_add_ps(sYOffset, sImaginary);

#ifdef  ENABLE_SOMETHING_REALLY_REALLY_REALLY_STUPID
					if ( SeriouslyYouDontWantToEnableThis )
					{
						// sOldReal used as temporary variable
						sOldReal			=	sImaginary;
						sImaginary			=	sReal;
						sReal				=	sOldReal;
					}
#endif

					for ( Count = 0; Count < TRIES_PER_PIXEL; Count++ )
					{
						sOldReal			=	sZReal;

						sZReal				=	_mm_mul_ps(sZReal, sZReal);
						sZReal				=	_mm_sub_ps(sZReal, _mm_mul_ps(sZImaginary, sZImaginary));
						sZReal				=	_mm_add_ps(sZReal, sReal);

						sZImaginary			=	_mm_mul_ps(sZImaginary, _mm_mul_ps(sTwo, sOldReal));
						sZImaginary			=	_mm_add_ps(sZImaginary, sImaginary);

						// Calculate whether we bother to go on
						
						//// Check if we are in an area where we know for sure that we are not approaching infinity
						//if ( false && Count < 3 )
						//{
						//	sOldReal			=	_mm_add_ps(_mm_mul_ps(sZReal, sZReal), _mm_mul_ps(sZImaginary, sZImaginary) );
						//	
						//	// Check if we for sure know that there is nothing going on here.
						//	for ( size_t N = 0; N < 4; N++ )
						//	{
						//		float	Value	=	GetSIMDFloat(sOldReal, N);

						//		if ((DoneWith[N] == false) && (
						//			(Count == 0 && Value < 0.0625 ) ||
						//			(Count == 1 && Value < 0.035226546f ) ||
						//			(Count == 2 && Value < 0.0010055788f ) ) )
						//		{
						//			DoneWith[N]		=	true;
						//			sCount[N]		=	0;
						//		}
						//	}		
						//}

						// (We refuse sOldReal to save memory)
						//sOldReal			=	_mm_add_ps(_mm_mul_ps(sZReal, sZReal), _mm_add_ps(sZImaginary, sZImaginary) );
						sOldReal			=	_mm_add_ps(_mm_mul_ps(sZReal, sZReal), _mm_mul_ps(sZImaginary, sZImaginary) );

#ifdef SSE2_DoneWith
						DoneWith			=	_mm_or_ps(DoneWith, _mm_cmpgt_ps(sOldReal, sFour));
#else
						DoneWith[0]			=	DoneWith[0] || GetSIMDFloat(sOldReal,0) > 4.0f;
						DoneWith[1]			=	DoneWith[1] || GetSIMDFloat(sOldReal,1) > 4.0f;
						DoneWith[2]			=	DoneWith[2] || GetSIMDFloat(sOldReal,2) > 4.0f;
						DoneWith[3]			=	DoneWith[3] || GetSIMDFloat(sOldReal,3) > 4.0f;
#endif

						//_mm_cmpgt_ps(sOldReal, sFour);

#ifdef SSE2_DoneWith
						if ( GetSIMDBool(DoneWith, 0) && GetSIMDBool(DoneWith, 1) && GetSIMDBool(DoneWith, 2) && GetSIMDBool(DoneWith, 3) )
#else
						if ( DoneWith[0] && DoneWith[1] && DoneWith[2] && DoneWith[3] )
#endif
						{
							// All the sample points have now 'approached' 'infinity', break loop!
							break;
						}
						
						// The else is to avoid overflows.
						if ( GetSIMDFloat(sOldReal,0) <= 4.0f )	{ sCount[0]++; } else { SetSIMDFloat(sZReal, 0, 4.0f); SetSIMDFloat(sZImaginary, 0, 4.0f); }

						if ( GetSIMDFloat(sOldReal,1) <= 4.0f ) { sCount[1]++; } else { SetSIMDFloat(sZReal, 1, 4.0f); SetSIMDFloat(sZImaginary, 1, 4.0f); }
						if ( GetSIMDFloat(sOldReal,2) <= 4.0f ) { sCount[2]++; } else { SetSIMDFloat(sZReal, 2, 4.0f); SetSIMDFloat(sZImaginary, 2, 4.0f); }
						if ( GetSIMDFloat(sOldReal,3) <= 4.0f ) { sCount[3]++; } else { SetSIMDFloat(sZReal, 3, 4.0f); SetSIMDFloat(sZImaginary, 3, 4.0f); }
					}

					#ifdef ENABLE_SOMETHING_REALLY_REALLY_REALLY_STUPID

						unsigned int R_A = 64;
						unsigned int R_B = 4;
						unsigned int G_A = 128;
						unsigned int G_B = 2;
						unsigned int B_A = 256;
						unsigned int B_B = 1;

						if ( SeriouslyYouDontWantToEnableThis )
						{
							R_A = 64;
							R_B = 4;
							G_A = 128;
							G_B = 1;
							B_A = 128;
							B_B = 1;
						}

					#else

						#define R_A 64
						#define R_B 4
						#define G_A 128
						#define G_B 2
						#define B_A 256
						#define B_B 1

					#endif


					//if ( TRIES_PER_PIXEL > 255 ) { Tries = 255; } else { Tries = TRIES_PER_PIXEL; }
					unsigned int Tries = TRIES_PER_PIXEL;

					// Give stuff a color!
					for ( size_t N = 0; N < 4; N++ )
					{
						if ( sCount[N] != TRIES_PER_PIXEL )
						{
							if ( sCount[N] > 255 ) { sCount[N] = 255; }

							iColorness = sCount[N] * 256 / Tries;

							if ( iColorness < B_A )
							{
								ColorB			+=	B_B*iColorness;

								if ( iColorness < G_A )
								{
									ColorG		+=	G_B*iColorness;

									if ( iColorness < R_A )
									{
										ColorR	+=	R_B*iColorness;
									}
									else
									{
										ColorR	+=	255;
									}
								}
								else
								{
									ColorG		+=	255;
									ColorR		+=	255;
								}
							}
							else
							{
								ColorB			+=	255;
								ColorG			+=	255;
								ColorR			+=	255;
							}
						}
					}
				}

#else
				XOffset	=	WINDOW_X+PixelW*X;
				YOffset	=	WINDOW_Y+PixelH*Y;

				for ( unsigned int Ax = 0; Ax < (ANTIALIAS_QUALITY*ANTIALIAS_QUALITY); Ax++ )
				{
					In.Real			=	XOffset+(AW*(Ax%ANTIALIAS_QUALITY));
					In.Imaginary	=	YOffset+(AH*(Ax/ANTIALIAS_QUALITY));

					float	OldReal;
					ComplexFloat z(0,0);

					for ( Count = 0; Count < TRIES_PER_PIXEL; Count++ )
					{
						OldReal				=	z.Real;
						z.Real				*=	z.Real;
						z.Real				-=	z.Imaginary*z.Imaginary - In.Real;
						z.Imaginary			*=	2.0f*OldReal;
						z.Imaginary			+=	In.Imaginary;
							
						if ( 
							// Todo, we have some magic constants here, they should be more 'precise'! :D
							(Count == 0 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.0625 ) ||
							(Count == 1 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.035226546f ) ||
							(Count == 2 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.0010055788f ) /*||
							(Count == 3 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.00019491167f ) ||
							(Count == 4 && z.Imaginary*z.Imaginary+z.Real*z.Real < 0.00021877403f )*/  )
						{
							//ZCalls	+=	Count+1;
							Count	=	-1;
							break;
						}
						
						if ( z.Real*z.Real+z.Imaginary+z.Imaginary > 4.0f )
						{
							break;
						}
					}

					if ( Count != ULONG_MAX )
					{
						Count++;
						ZCalls	+=	Count+1;

						if ( Count == TRIES_PER_PIXEL+1 ) { Count = 0; }
					}

					if ( Count != ULONG_MAX )
					{
						if ( Count > 255 ) { Count = 255; }

						int	Tries;

						if ( TRIES_PER_PIXEL > 255 ) { Tries = 255; } else { Tries = TRIES_PER_PIXEL; }

						iColorness = Count * 256 / Tries;

						if ( iColorness < 256 )
						{
							ColorB			+=	iColorness;

							if ( iColorness < 128 )
							{
								ColorG		+=	2*iColorness;

								if ( iColorness < 64 )
								{
									ColorR	+=	4*iColorness;
								}
								else
								{
									ColorR	+=	255;
								}
							}
							else
							{
								ColorG		+=	255;
								ColorR		+=	255;
							}
						}
						else
						{
							ColorB			+=	255;
							ColorG			+=	255;
							ColorR			+=	255;
						}
					}

					if ( false && Count != ULONG_MAX  )
					{
						if ( LastCount == Count )
						{
							Colorness		=	LastSQRTCount*invsqrtTRIES_PER_PIXEL;
						}
						else
						{
							CountSQRT		=	sqrt((float)Count);
							LastSQRTCount	=	CountSQRT;
							Colorness		=	CountSQRT*LastSQRTCount;

							LastCount		=	Count;
						}

						iColorness		=	(unsigned int)128.0f*Colorness;

						if ( iColorness > 255 ) { iColorness = 255; }

						ColorR			+=	iColorness;
						ColorG			+=	iColorness;
						ColorB			+=	iColorness;

						//ColorR			+=	min((unsigned int)(4*iColorness),255);
						//ColorG			+=	min((unsigned int)(2*iColorness),255);
						//ColorB			+=	min((unsigned int)(1*iColorness),255);

					//	if ( iColorness < 256 )
					//	{
					//		ColorB			+=	iColorness;

					//		if ( iColorness < 128 )
					//		{
					//			ColorG		+=	2*iColorness;

					//			if ( iColorness < 64 )
					//			{
					//				ColorR	+=	4*iColorness;
					//			}
					//			else
					//			{
					//				ColorR	+=	255;
					//			}
					//		}
					//		else
					//		{
					//			ColorG		+=	255;
					//			ColorR		+=	255;
					//		}
					//	}
					//	else
					//	{
					//		ColorB			+=	255;
					//		ColorG			+=	255;
					//		ColorR			+=	255;
					//	}
					}
				}
#endif

				ColorR /= AAQSQR;
				ColorG /= AAQSQR;
				ColorB /= AAQSQR;

				BackBuffer[Y*rectClient.right+X] = RGB(ColorB,ColorG,ColorR);
			}	
		}

		SetEvent(ThreadDone[ThreadId]);

		WaitForSingleObject(DrawedEvent,INFINITE);
	}

	return 0;
}

DWORD_PTR WINAPI Imagine(LPVOID p)
{
	DEVMODE Dev;
	BOOL	Result	=	EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&Dev);
	int w = Dev.dmPelsWidth/2;
	int h = Dev.dmPelsHeight/2;
	int x = (Result) ? (int)(Dev.dmPelsWidth - w ) / 2 : (int)CW_USEDEFAULT;
	int y = (Result) ? (int)(Dev.dmPelsHeight - h ) / 2 : (int)CW_USEDEFAULT;

	while ( true )
	{
		WaitForSingleObject(DrawRequest,INFINITE);
		ResetEvent(DrawRequest);

		HWND	hWnd	=	Application.WindowHWND;

		PAINTSTRUCT ps          = {0};
		HDC hDC = BeginPaint (hWnd, &ps);

		GetClientRect(hWnd, &rectClient);

		if ( Buffer1 == NULL ) { Buffer1 = new COLORREF[Dev.dmPelsWidth*Dev.dmPelsHeight]; BufferWidth = Dev.dmPelsWidth; BufferHeight = Dev.dmPelsHeight; }
		if ( Buffer2 == NULL ) { Buffer2 = new COLORREF[Dev.dmPelsWidth*Dev.dmPelsHeight]; BufferWidth = Dev.dmPelsWidth; BufferHeight = Dev.dmPelsHeight; }

		BackBuffer	=	Buffer1;
		FrontBuffer	=	Buffer2;

		DrawEvent	=	CreateEvent(NULL,TRUE,FALSE,NULL);
		DrawedEvent	=	CreateEvent(NULL,TRUE,FALSE,NULL);


		ThreadDone	=	new HANDLE[nThreads];
		Thread		=	new HANDLE[nThreads];

		for ( DWORD_PTR I = 0; I < nThreads; I++ )
		{
			ThreadDone[I]	=	CreateEvent(NULL,TRUE,FALSE,NULL);
			Thread[I]		=	CreateThread(NULL,NULL,ImagineThread,(LPVOID)I,0,0);
		}

		
		DWORD	Start		=	GetTickCount();
		DWORD	End			=	GetTickCount();

		ResetEvent(DrawedEvent);
		SetEvent(DrawEvent);

	#ifdef ANIMATE
		while ( true )
		{
	#endif
			WaitForMultipleObjects(nThreads,ThreadDone,TRUE,INFINITE);

			GetClientRect (hWnd, &rectClient);
		
			End			=	GetTickCount();


			double	FPS		=	1.0f/((End-Start)/1000.0f);

			#ifdef ANIMATE
			#ifdef ANIMATE_ZOOM

				float	DeltaTime		=	(End-Start)/1000.0f;
				float	SpeedFactor		=	1.0f-(1.0f-ZOOM_SPEED)*DeltaTime;

				WINDOW_X	=	((WINDOW_X-ZOOMTO_X) * SpeedFactor)+ZOOMTO_X;
				WINDOW_Y	=	((WINDOW_Y-ZOOMTO_Y) * SpeedFactor)+ZOOMTO_Y;
				WINDOW_H	=	WINDOW_H * SpeedFactor;
				WINDOW_W	=	WINDOW_W * SpeedFactor;

			#else
				
				float	DeltaTime		=	(End-Start)/1000.0f;
				float	SpeedFactor		=	1.0f-(1.0f-ZOOM_SPEED)*DeltaTime;

				WINDOW_X	=	((WINDOW_X-WINDOW_IDEAL_X) * SpeedFactor)+WINDOW_IDEAL_X;
				WINDOW_Y	=	((WINDOW_Y-WINDOW_IDEAL_Y) * SpeedFactor)+WINDOW_IDEAL_Y;
				WINDOW_W	=	((WINDOW_W-WINDOW_IDEAL_W) * SpeedFactor)+WINDOW_IDEAL_W;
				WINDOW_H	=	((WINDOW_H-WINDOW_IDEAL_H) * SpeedFactor)+WINDOW_IDEAL_H;

			#endif

			#ifdef ANIMATE_ADJUST_QUALITY_TO_FPS

			unsigned int MIN_QUALITY = 40;

#ifdef QUALITY_IS_ADJUSTABLE
			MIN_QUALITY		+=	TRIES_PER_PIXEL_ADDED;
#endif

			if ( MIN_QUALITY > MAX_QUALITY ) { MIN_QUALITY = MAX_QUALITY; }

			if ( FPS < IDEAL_FPS )
			{
				if ( TRIES_PER_PIXEL > MIN_QUALITY )
				//if ( (nThreads < 4 && TRIES_PER_PIXEL > 20) || (nThreads == 4 && TRIES_PER_PIXEL > 30)  || (nThreads > 4 && TRIES_PER_PIXEL > 80))
				{
					TRIES_PER_PIXEL		=	(unsigned int)(TRIES_PER_PIXEL*FPS_ADJUST_SPEED);
				}
			}
			else if ( FPS > PERFECT_FPS )
			{
				if ( TRIES_PER_PIXEL < MAX_QUALITY )
				{
					TRIES_PER_PIXEL		=	(unsigned int)(TRIES_PER_PIXEL/FPS_ADJUST_SPEED);
				}
				else
				{
					TRIES_PER_PIXEL		=	MAX_QUALITY;
				}
			}

			if ( MIN_QUALITY > TRIES_PER_PIXEL ) { TRIES_PER_PIXEL = MIN_QUALITY; }

			#endif
			#endif


			ResetEvent(DrawEvent);

			#ifdef SSE2
				#define COMPILE_STRING _MESTR("SSE2 ")
			#elif defined(SSE)
				#define COMPILE_STRING _MESTR("SSE ")
			#else
				#define COMPILE_STRING _MESTR("")
			#endif

			MESTR*	QualityString	=	UIToA(TRIES_PER_PIXEL);

			if ( FPS > 1.0f )
			{
				MESTR*	FPSStr	=	DToA(FPS, 3);

				MESTR*	WindowTitle		=	BuildString(7, _MESTR("Maxsi ImaginationLand "), COMPILE_STRING, _MESTR("Q="), QualityString, _MESTR(" "),  FPSStr, _MESTR(" fps"));

				SetWindowTextA(hWnd, WindowTitle);

				delete[]	WindowTitle;
				delete[]	FPSStr;
			}
			else
			{
				FPS				=	1/FPS;
				MESTR*	FPSStr	=	DToA(FPS, 5);

				MESTR*	WindowTitle		=	BuildString(7, _MESTR("Maxsi ImaginationLand "), COMPILE_STRING, _MESTR("Q="), QualityString, _MESTR(" "), FPSStr, _MESTR(" seconds per frame"));

				SetWindowTextA(hWnd, WindowTitle);

				delete[]	WindowTitle;
				delete[]	FPSStr;
			}

			delete[]	QualityString;

			for ( DWORD_PTR I = 0; I < nThreads; I++ )
			{
				ResetEvent(ThreadDone[I]);
			}

			COLORREF*	Temp		=	BackBuffer;
						BackBuffer	=	FrontBuffer;
						FrontBuffer	=	Temp;

			SetEvent(DrawedEvent);
			Start		=	GetTickCount();

			ResetEvent(DrawedEvent);
			SetEvent(DrawEvent);

#ifdef CACHE_BACKBUFFER_HANDLE
			if ( DrawBackBuffer )
			{
				if ( DrawBackBufferW != rectClient.right || DrawBackBufferH != rectClient.bottom )
				{
					DeleteObject(DrawBackBuffer);

					DrawBackBuffer		= NULL;
				}
			}
			if ( DrawBackBuffer == NULL )
			{
				DrawBackBuffer			=	CreateCompatibleBitmap(hDC, rectClient.right, rectClient.bottom);
				DrawBackBufferW			=	rectClient.right;
				DrawBackBufferH			=	rectClient.bottom;
			}
#else
			HBITMAP		DrawBackBuffer	=	CreateCompatibleBitmap(hDC, rectClient.right, rectClient.bottom);
#endif

			BITMAPINFO	DrawBackBufferInfo;
			DrawBackBufferInfo.bmiHeader.biBitCount = 32;
			DrawBackBufferInfo.bmiHeader.biClrImportant = 0;
			DrawBackBufferInfo.bmiHeader.biClrUsed = 0;
			DrawBackBufferInfo.bmiHeader.biCompression = BI_RGB;
			DrawBackBufferInfo.bmiHeader.biHeight = 0-rectClient.bottom;
			DrawBackBufferInfo.bmiHeader.biPlanes = 1;
			DrawBackBufferInfo.bmiHeader.biSize = sizeof(DrawBackBufferInfo);
			DrawBackBufferInfo.bmiHeader.biSizeImage = BI_RGB;
			DrawBackBufferInfo.bmiHeader.biWidth = rectClient.right;
			DrawBackBufferInfo.bmiHeader.biXPelsPerMeter = 0;
			DrawBackBufferInfo.bmiHeader.biYPelsPerMeter = 0;

			SetDIBits(hDC,DrawBackBuffer,0,rectClient.bottom,FrontBuffer,&DrawBackBufferInfo, DIB_RGB_COLORS );

			BITMAP    Bitmap;

			HDC hDCBits = CreateCompatibleDC(hDC);
			GetObject(DrawBackBuffer, sizeof(BITMAP), (LPSTR)&Bitmap);
			SelectObject(hDCBits, DrawBackBuffer);
			BitBlt(hDC, 0, 0, rectClient.right, rectClient.bottom, hDCBits, 0, 0, SRCCOPY);
			DeleteDC(hDCBits);

#ifndef CACHE_BACKBUFFER_HANDLE
			DeleteObject(DrawBackBuffer);
#endif

	#ifdef ANIMATE
		}
	#endif
		
		WaitForMultipleObjects(nThreads,ThreadDone,TRUE,INFINITE);

		for ( DWORD_PTR I = 0; I < nThreads; I++ )
		{
			CloseHandle(ThreadDone[I]);
		}

		delete[]	ThreadDone; ThreadDone = NULL;

		CloseHandle(DrawEvent);

		EndPaint (hWnd, &ps);
	}

	return (DWORD_PTR)NULL;
}

void BenchMarkZ()
{
	ComplexFloat C(0.0f,2.0f);
	ComplexFloat z(0.0f,0.0f);

	DWORD	Start	=	GetTickCount();

	float	OldReal;

	for ( size_t N = 0; N < 100000000; N++ )
	{
#if 0 //3.7 MHz
		z = Z(z,C);
#elif 0 // 4.7 MHz
		z = (z*z)+C;
#elif 0 // 7.4 MHz
		z *= z;
		z += C;
#elif 1 // 50 MHz

		//float	OldA	=	A;
		//float	OldC	=	C;

		//A		=	(A*C) - (B*D);
		//B		=	(OldA*D) + (B*OldC);

		// If A == C && B == D then:

		//float	OldA	=	A;

		//A		=	(A*A) - (B*B);
		//B		=	(OldA*B) + (B*OldA);


		//float	OldA	=	A;

		//A		*=	A;
		//A		-=	(B*B);
		//B		=	2*OldA*B;


		//float	OldA	=	A;

		//A		*=	A;
		//A		-=	(B*B);
		//B		*=	2*OldA;


		OldReal		=	z.Real;
		z.Real		*=	z.Real;
		z.Real		-=	z.Imaginary*z.Imaginary + C.Real;
		z.Imaginary	*=	2.0f*OldReal;
		z.Imaginary +=	C.Imaginary;
#endif
	}

	//DWORD	Time	=	GetTickCount()-Start;

	//double	Speed	=	100000000.0f/(Time/1000.0f);

	return;
}

void ZResearch()
{
	return;
}


void TestSlowInvSqrt()
{
	DWORD	Start	=	GetTickCount();

	float	sqrtTRIES_PER_PIXEL;

	for ( size_t N = 0; N < 100000000; N++ )
	{
		sqrtTRIES_PER_PIXEL		=	1.0f/sqrt((float)TRIES_PER_PIXEL);
	}

	//DWORD	Time	=	GetTickCount()-Start;

	//double	Speed	=	100000000.0f/(Time/1000.0f);

	return;
}


void TestFastInvSqrt()
{
	DWORD	Start	=	GetTickCount();


	float	sqrtTRIES_PER_PIXEL;
	float	sqrtTRIES_PER_PIXELhalf;
	int i;

	for ( size_t N = 0; N < 100000000; N++ )
	{
		sqrtTRIES_PER_PIXEL		=	(float)TRIES_PER_PIXEL;
		sqrtTRIES_PER_PIXELhalf	=	 0.5f*sqrtTRIES_PER_PIXEL;
		i = *(int*)&sqrtTRIES_PER_PIXEL; // get bits for floating value
		i = 0x5f3759df - (i>>1); // gives initial guess y0
		sqrtTRIES_PER_PIXEL = *(float*)&i; // convert bits back to float
		sqrtTRIES_PER_PIXEL = sqrtTRIES_PER_PIXEL*(1.5f-sqrtTRIES_PER_PIXELhalf*sqrtTRIES_PER_PIXEL*sqrtTRIES_PER_PIXEL); // Newton step, repeating increases accuracy
	}

	//DWORD	Time	=	GetTickCount()-Start;

	//double	Speed	=	100000000.0f/(Time/1000.0f);

	return;
}

#endif

